using System;
using System.ComponentModel;

namespace HalconDotNet
{
	public class HHandleBase : IDisposable
	{
		[EditorBrowsable(EditorBrowsableState.Never)]
		public static readonly IntPtr UNDEF = IntPtr.Zero;

		[EditorBrowsable(EditorBrowsableState.Never)]
		public static readonly HHandle HNULL = new HHandle();

		private IntPtr mHandle;

		private bool suppressedFinalization;

		[EditorBrowsable(EditorBrowsableState.Never)]
		public IntPtr Handle
		{
			get
			{
				return mHandle;
			}
			set
			{
				SetHandleInternal(value, copy: true);
				HalconAPI.ReleaseExternalOwnership(value);
			}
		}

		internal HHandleBase()
			: this(UNDEF)
		{
		}

		[EditorBrowsable(EditorBrowsableState.Never)]
		internal HHandleBase(IntPtr handle)
		{
			Handle = handle;
		}

		private void SetHandleInternal(IntPtr handle, bool copy)
		{
			ClearHandleInternal();
			if (suppressedFinalization)
			{
				suppressedFinalization = false;
				GC.ReRegisterForFinalize(this);
			}
			if (handle != UNDEF)
			{
				mHandle = (copy ? HalconAPI.CopyHandle(handle) : handle);
			}
		}

		internal HHandleBase(HHandleBase handle)
		{
			SetHandleInternal(handle, copy: true);
		}

		[EditorBrowsable(EditorBrowsableState.Never)]
		public void Detach()
		{
			HalconAPI.AcquireExternalOwnership(mHandle);
		}

		public bool IsInitialized()
		{
			return HalconAPI.HandleIsValid(mHandle);
		}

		~HHandleBase()
		{
			try
			{
				Dispose(disposing: false);
			}
			catch (Exception)
			{
			}
		}

		private void Dispose(bool disposing)
		{
			if (mHandle != UNDEF)
			{
				ClearHandleInternal();
				mHandle = UNDEF;
			}
			if (disposing)
			{
				GC.SuppressFinalize(this);
				suppressedFinalization = true;
			}
			GC.KeepAlive(this);
		}

		void IDisposable.Dispose()
		{
			Dispose(disposing: true);
		}

		public virtual void Dispose()
		{
			Dispose(disposing: true);
		}

		[EditorBrowsable(EditorBrowsableState.Never)]
		public void InvalidateWithoutDispose()
		{
			Detach();
			Dispose();
		}

		internal void Store(IntPtr proc, int parIndex)
		{
			HalconAPI.StoreH(proc, parIndex, mHandle);
		}

		internal int Load(IntPtr proc, int parIndex, int err)
		{
			if (mHandle != UNDEF)
			{
				throw new HalconException("Undisposed handle instance when loading output parameter");
			}
			if (HalconAPI.IsFailure(err))
			{
				return err;
			}
			err = HalconAPI.LoadH(proc, parIndex, err, out HHandle handleValue);
			SetHandleInternal(handleValue.Handle, copy: true);
			handleValue.Dispose();
			return err;
		}

		protected virtual void ClearHandleInternal()
		{
			if (mHandle != UNDEF)
			{
				HalconAPI.ClearHandle(mHandle);
				mHandle = UNDEF;
			}
		}

		public override string ToString()
		{
			if (mHandle == UNDEF)
			{
				return "";
			}
			return "H" + mHandle.ToInt64().ToString("X");
		}

		protected internal void AssertSemType(string sem_type)
		{
			if (!HalconAPI.IsLegacyHandleMode() && mHandle != UNDEF)
			{
				string handleSemType = HalconAPI.GetHandleSemType(mHandle);
				if (!sem_type.Equals(handleSemType))
				{
					throw new HalconException("Invalid handle instance passed");
				}
			}
			GC.KeepAlive(this);
		}

		[EditorBrowsable(EditorBrowsableState.Never)]
		public static HTuple ConcatArray(HHandleBase[] handles)
		{
			return new HTuple(handles as HHandle[]);
		}

		public static implicit operator IntPtr(HHandleBase handle)
		{
			return handle?.mHandle ?? UNDEF;
		}
	}
}
